import java.util.concurrent.locks.Lock;

public class MoveRacer implements Runnable{

		Racer racer;
		ComponentPanel[][] componentpanel;
		int componentPanelSize;
		Lock lock;
		int episode = 1;
		
		public MoveRacer(Racer racer,ComponentPanel[][] c , int cSize,Lock lock){
			
			this.lock = lock;
			this.racer=racer;
			this.componentpanel=c;
			this.componentPanelSize = cSize;
			
		}
		
		public synchronized void run() {
			
			try {
				
				while(true){
					
					
					Thread.sleep(Racer.volume);
					
					if(DetectPolice()){

						racer.qValues = racer.qHiddenValues;
						racer.oldState = racer.currentState;
						
						if(racer.nextState==null)
						   racer.currentState = findNextState(racer.oldState);
						else
						   racer.currentState=racer.nextState;
						lock.lock();
						
						if(racer.oldState!=racer.currentState && !(componentpanel[racer.currentState.x][racer.currentState.y].component instanceof Target)){		
							componentpanel[racer.oldState.x][racer.oldState.y].component=new Empty();
							componentpanel[racer.oldState.x][racer.oldState.y].putComponent(componentpanel[racer.oldState.x][racer.oldState.y].component.getImage());
						
						racer.counter++;
						racer.nextState = findNextState(racer.currentState);
						componentpanel[racer.nextState.x][racer.nextState.y].component=racer;
						
						if(componentpanel[racer.currentState.x][racer.currentState.y].isHidden){
							racer.temporal.reward = 10;
							racer.temporal.Learn(racer);
							racer.temporal.reward = 0;
							boolean b = true;
							while(DetectPolice()){
								
							}
						}
						else
							racer.temporal.Learn(racer);
					
						componentpanel[racer.currentState.x][racer.currentState.y].component=racer;
						componentpanel[racer.currentState.x][racer.currentState.y].putComponent(componentpanel[racer.currentState.x][racer.currentState.y].component.getImage());
						
						lock.unlock();
						}
						else{
							do{
							racer.nextState = findRandomWay(racer.currentState);
							}while(!(componentpanel[racer.nextState.x][racer.nextState.y].component instanceof Empty));
						}
							
					}
					
					else{
						
						racer.qValues = racer.qTargetValues;
						racer.oldState = racer.currentState;
							
						if(racer.nextState==null)
							racer.currentState = findNextState(racer.oldState);
						else
							racer.currentState=racer.nextState;
						
						synchronized (componentpanel) {
					
						if(racer.oldState!=racer.currentState){
							
								componentpanel[racer.oldState.x][racer.oldState.y].component=new Empty();
							    componentpanel[racer.oldState.x][racer.oldState.y].putComponent(componentpanel[racer.oldState.x][racer.oldState.y].component.getImage());
							
						}
						
						racer.nextState = findNextState(racer.currentState);
						
						racer.counter++;
						
						if(componentpanel[racer.currentState.x][racer.currentState.y].component instanceof Target){
							startNewEpisode();
						}
						else 
							racer.temporal.Learn(racer);
						
                            
						
						componentpanel[racer.currentState.x][racer.currentState.y].component=racer;
						componentpanel[racer.currentState.x][racer.currentState.y].putComponent(componentpanel[racer.currentState.x][racer.currentState.y].component.getImage());
					    
						if(racer.nextState!=null)
						if((componentpanel[racer.nextState.x][racer.nextState.y].component instanceof Empty)  )
						componentpanel[racer.nextState.x][racer.nextState.y].component=racer;
						}
						    
						
					}
					
				}
				
			} catch (InterruptedException e) {
					e.printStackTrace();
				}
		}
		public  void printQValues(){
			for(int i = 0;i<4;i++)
				System.out.print(racer.qValues[racer.currentState.x][racer.currentState.y][i]+"\t");
			System.out.println();
		}
		public synchronized boolean DetectPolice(){
			int firstx,firsty,secondx,secondy;
			
			firstx = secondx = racer.currentState.x;
			firsty = secondy = racer.currentState.y;
			int step=2;
			
			while(firstx>0 && step>0){
				firstx--;
				step--;
			}
			step=2;
          
			while(firsty>0 && step>0){
				firsty--;
				step--;
			}
			step=2;
           
           while(secondx<componentPanelSize-1 && step>0){
        	   secondx++;
        	   step--;
           }
           step=2;
           
           while(secondy<componentPanelSize-1 && step>0){
        	   secondy++;
        	   step--;
           }
           
           for(int i=firstx; i<=secondx; i++)
        	   for(int j=firsty; j<=secondy; j++){
        		   if(componentpanel[i][j].component instanceof Police)
        			   return true;
        		   
        	   }
			
			return false;
		}
		
		public State setState(State st,int x, int y,int action){
			
			State state = new State(st.x + x,st.y + y);
			
			st.action=action;
			return state;
		}
		public  int findMaxDoor(State st){
			int k = 0;
			double value = -1000000;
			for (int i=0;i<4;i++){
				if(racer.qValues[st.x][st.y][i] > value){
					value=racer.qValues[st.x][st.y][i];
					k=i;
				}
			}
			return k;
		}
		public State findRandomWay(State st){
			State state;
			while(true){
				
				int random = (int) (Math.random()*4);	
				//System.out.println(random +" (" + racer.currentState.x +""+racer.currentState.y+")");
				if(random == 0 && st.x>0 &&((componentpanel[st.x-1][st.y].component instanceof Empty)||(componentpanel[st.x-1][st.y].component instanceof Target) || componentpanel[st.x-1][st.y].isHidden)){
					state = setState(st,-1,0,0);
					break;
				}
				else if(random == 1 && st.y>0 && ((componentpanel[st.x][st.y-1].component instanceof Target) || (componentpanel[st.x][st.y-1].component instanceof Empty) || componentpanel[st.x][st.y-1].isHidden)){
					state=setState(st,0,-1,1);
					break;
				}
				else if(random == 2 && st.x<componentPanelSize-1 && ((componentpanel[st.x+1][st.y].component instanceof Target) || (componentpanel[st.x+1][st.y].component instanceof Empty) || componentpanel[st.x+1][st.y].isHidden) ){
					state=setState(st,1,0,2);
					break;
				}	
				else if(random == 3 && st.y<componentPanelSize-1 && ( (componentpanel[st.x][st.y+1].component instanceof Target) || (componentpanel[st.x][st.y+1].component instanceof Empty) || componentpanel[st.x][st.y+1].isHidden)){
					state=setState(st,0,1,3);
					break;
				}
			}
			return state;
		}
		
		public synchronized State findNextState(State st){
			
			State state;
			
			if(racer.qValues[st.x][st.y][0] == (racer.qValues[st.x][st.y][1]) &&
			   racer.qValues[st.x][st.y][0] == (racer.qValues[st.x][st.y][2]) &&
			   racer.qValues[st.x][st.y][0] == (racer.qValues[st.x][st.y][3])){
						state = findRandomWay(st);
			}
			
			else{
				
				double randomDouble = Math.random();
				
				if(randomDouble > racer.temporal.epsilon){
					int tempK = -1 ;double temp = 0;
					System.out.println(racer.temporal.epsilon);
					while(true){
						
						int k  = findMaxDoor(st);
						if(k==0){
							if(st.x>0){
								
								if((componentpanel[st.x-1][st.y].component instanceof Target) || (componentpanel[st.x-1][st.y].component instanceof Empty)|| componentpanel[st.x-1][st.y].isHidden){		
									if(tempK!=-1)
										racer.qValues[st.x][st.y][tempK] = temp;
									state = setState(st,-1,0,0);
									break;
								}
								else{
									tempK = k;
									temp = racer.qValues[st.x][st.y][k];
									racer.qValues[st.x][st.y][k] = -3000000;
								}
							}
							else
								racer.qValues[st.x][st.y][k]=-5000000;
							
						}
					
						else if(k==1){
							if(st.y>0){
								if((componentpanel[st.x][st.y-1].component instanceof Target) || (componentpanel[st.x][st.y-1].component instanceof Empty) || componentpanel[st.x][st.y-1].isHidden){
									if(tempK!=-1)
										racer.qValues[st.x][st.y][tempK] = temp;
									tempK = -1;
									state=setState(st,0,-1,1);
									break;
								}
								else{
									tempK = k;
									temp = racer.qValues[st.x][st.y][k];
									racer.qValues[st.x][st.y][k] = -3000000;
								}
							}
							else{
								racer.qValues[st.x][st.y][k]=-5000000;
							}
						}

						else if(k==2){
							if(st.x<componentPanelSize-1){
								if((componentpanel[st.x+1][st.y].component instanceof Target) || (componentpanel[st.x+1][st.y].component instanceof Empty) || componentpanel[st.x+1][st.y].isHidden){
									if(tempK!=-1)
										racer.qValues[st.x][st.y][tempK] = temp;
									tempK = -1;
									state=setState(st,1,0,2);
									break;
								}
								else{
									tempK = k;
									temp = racer.qValues[st.x][st.y][k];
									racer.qValues[st.x][st.y][k] = -3000000;
								}
							}
							else{
								racer.qValues[st.x][st.y][k]=-5000000;
							}
						}
					
						else if(k==3){
							if(st.y<componentPanelSize-1){
								if((componentpanel[st.x][st.y+1].component instanceof Target) || (componentpanel[st.x][st.y+1].component instanceof Empty) || componentpanel[st.x][st.y+1].isHidden){
									if(tempK!=-1)
										racer.qValues[st.x][st.y][tempK] = temp;
									tempK = -1;
									state=setState(st,0,1,3);
									break;
								}
								else{
									tempK = k;
									temp = racer.qValues[st.x][st.y][k];
									racer.qValues[st.x][st.y][k] = -3000000;
								}
							}
							else
								racer.qValues[st.x][st.y][k]=-5000000;
						}	
					
					}
				}
				
				else{
					state = findRandomWay(st);
				}
			}
			return state;
		}
		public void startNewEpisode(){
			racer.temporal.reward = 10;
			racer.total+=racer.counter;
			racer.temporal.Learn(racer);
			racer.temporal.reward = 0;
			System.out.println("racer :  "+ racer.which + "  episode num: "+ episode +"  trial:"  + racer.counter +"   average:"+(racer.total)/episode);
			racer.counter = 0;  
			racer.currentState.x = racer.initialState.x;
			racer.currentState.y = racer.initialState.y;
			racer.nextState = null;
			episode++;
			if(racer.temporal.isEpsilonDynamic)
				racer.temporal.epsilon = Math.exp(-1.2*episode*Math.log10(episode)/500);
		}
		
	}